#include <jni.h>
#include <string>
#include "player.h"
#include <android/native_window_jni.h>
#include <android/native_window.h>

static ANativeWindow *mANativeWindow;
static ANativeWindow_Buffer nwBuffer;
static jobject globalVideoSurfaceObject = NULL;
static jclass globalVideoSurfaceClass = NULL;
PlayContext playContext;
int player_status = WK_PLAYER_STATUS_IDLE;
static void sigterm_handler(int sig) {
    av_log(NULL, AV_LOG_ERROR, "sigterm_handler : sig is %d \n", sig);
    exit(123);
}

static void ffmpeg_log_callback(void *ptr, int level, const char *fmt,
                                va_list vl) {
    __android_log_vprint(ANDROID_LOG_DEBUG, "FFmpeg", fmt, vl);
}

extern "C" jint JNICALL Java_com_wukai_player_play_WKVideoPlayer_init(
        JNIEnv *env,
        jobject obj, jobject surface) {
    //初始化播放器
    initPlayer();
    jclass localVideoSurfaceClass = env->FindClass(
            "com/wukai/player/play/WKVideoPlayer");
    if (NULL == localVideoSurfaceClass) {
        LOGV("FindClass VideoSurface failure.");
        return -1;
    }

    globalVideoSurfaceClass = (jclass) env->NewGlobalRef(
            localVideoSurfaceClass);
    if (NULL == globalVideoSurfaceClass) {
        LOGV("localVideoSurfaceClass to globalVideoSurfaceClass failure.");
    }

    globalVideoSurfaceObject = (jclass) env->NewGlobalRef(obj);
    if (NULL == globalVideoSurfaceObject) {
        LOGV("obj to globalVideoSurfaceObject failure.");
    }

    if (NULL == surface) {
        LOGV("surface is null, destroy?");
        mANativeWindow = NULL;
        return 0;
    }

    // obtain a native window from a Java surface
    mANativeWindow = ANativeWindow_fromSurface(env, surface);
    LOGD("mANativeWindow ok");

    return 0;
};

extern "C" jint JNICALL Java_com_wukai_player_play_WKVideoPlayer_play(
        JNIEnv *env,
        jobject surface, jstring path) {

    playContext.videoFile = env->GetStringUTFChars(path,NULL);
    //初始化播放器
    pthread_t thread_open;
    pthread_create(&thread_open, NULL, open_video, NULL);
    return 0;
};



extern "C" jint JNICALL Java_com_wukai_player_play_WKVideoPlayer_pause(
        JNIEnv *env,
        jobject /* this */) {
    player_status = WK_PLAYER_STATUS_PAUSE;
    LOGI("pause....");
    return 0;
};

extern "C" jint JNICALL Java_com_wukai_player_play_WKVideoPlayer_resume(
        JNIEnv *env,
        jobject that /* this */) {
    LOGI("resume...");
    player_status = WK_PLAYER_STATUS_PLAYING;

    return 0;
};

extern "C" jint JNICALL Java_com_wukai_player_play_WKVideoPlayer_release(
        JNIEnv *env,
        jobject /* this */) {
    LOGI("release...");
    player_status = WK_PLAYER_STATUS_IDLE;
    AVFormatContext *ctx = playContext.fmt_ctx;
    if (ctx) {
        avformat_close_input(&ctx);
        avformat_free_context(ctx);
    }
    return 0;
};


int initPlayer() {
    if (player_status != WK_PLAYER_STATUS_IDLE) {
        LOGD("player inited");
        return 0;
    }
    LOGD("ffmpeg初始化");
    av_log_set_callback(ffmpeg_log_callback);
    // set log level
    av_log_set_level(AV_LOG_WARNING);
    /* register all codecs, demux and protocols */
    avfilter_register_all();
    av_register_all();
    avformat_network_init();
    playContext.video_queue = (PacketQueue *) malloc(sizeof(struct PacketQueue));
    memset(playContext.video_queue, 0, sizeof(struct PacketQueue));
    player_status = WK_PLAYER_STATUS_READY;

    LOGD("ffmpeg 初始化完成....");
    return 0;
}

void *open_video(void *args) {
    int video_stream_index = -1;
    playContext.fmt_ctx = avformat_alloc_context();
    int err = avformat_open_input(&playContext.fmt_ctx, playContext.videoFile, NULL, NULL);
    if (err < 0) {
        char errbuf[64];
        av_strerror(err, errbuf, 64);
        av_log(NULL, AV_LOG_ERROR, "avformat_open_input : err is %d , %s\n",
               err, errbuf);
        return 0;
    }

    if ((err = avformat_find_stream_info(playContext.fmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "avformat_find_stream_info : err is %d \n",
               err);
        return 0;
    }

    // search video stream in all streams.
    for (int i = 0; i < playContext.fmt_ctx->nb_streams; i++) {
        if (playContext.fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    // if no video and audio, exit
    if (-1 == video_stream_index) {
        return 0;
    }

    // open video
    playContext.vcodec_ctx = playContext.fmt_ctx->streams[video_stream_index]->codec;
    playContext.vstream = playContext.fmt_ctx->streams[video_stream_index];
    playContext.vcodec = avcodec_find_decoder(
            playContext.vcodec_ctx->codec_id);
    if (NULL == playContext.vcodec) {
        av_log(NULL, AV_LOG_ERROR,
               "avcodec_find_decoder video failure. \n");

    }

    if (avcodec_open2(playContext.vcodec_ctx, playContext.vcodec,
                      NULL) < 0) {
        av_log(NULL, AV_LOG_ERROR, "avcodec_open2 failure. \n");

    }

    if ((playContext.vcodec_ctx->width > 0)
        && (playContext.vcodec_ctx->height > 0)) {

        setBuffersGeometry(playContext.vcodec_ctx->width,
                           playContext.vcodec_ctx->height);
    }

    av_log(NULL, AV_LOG_ERROR, "video : width is %d, height is %d . \n",
           playContext.vcodec_ctx->width,
           playContext.vcodec_ctx->height);

    pthread_t play_thread;
    pthread_create(&play_thread, NULL, decode_video, NULL);
    player_status = WK_PLAYER_STATUS_PLAYING;

    // read url media data circle
    AVPacket pkt;
    while ((av_read_frame(playContext.fmt_ctx, &pkt) >= 0) &&
           (player_status == WK_PLAYER_STATUS_PLAYING)) {
        if (pkt.stream_index == video_stream_index) {
            packet_queue_put(playContext.video_queue, &pkt);
        } else {
            av_packet_unref(&pkt);
        }
    }
    return ((void *) 0);
}


void renderSurface(uint8_t *pixel) {

    if (player_status == WK_PLAYER_STATUS_PAUSE) {
        return;
    }

    ANativeWindow_acquire(mANativeWindow);

    if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, NULL)) {
        LOGV("ANativeWindow_lock() error");
        return;
    }
    //LOGV("renderSurface, %d, %d, %d", nwBuffer.width ,nwBuffer.height, nwBuffer.stride);
    if (nwBuffer.width >= nwBuffer.stride) {
        memcpy(nwBuffer.bits, pixel, nwBuffer.width * nwBuffer.height * 2);
    } else {
        LOGV("new buffer width is %d,height is %d ,stride is %d",
             nwBuffer.width, nwBuffer.height, nwBuffer.stride);
        int i;
        for (i = 0; i < nwBuffer.height; ++i) {
            memcpy((void *) ((int *) nwBuffer.bits + nwBuffer.stride * i * 2),
                   (void *) ((int) pixel + nwBuffer.width * i * 2),
                   nwBuffer.width * 2);
        }
    }

    if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
        LOGV("ANativeWindow_unlockAndPost error");
        return;
    }

    ANativeWindow_release(mANativeWindow);
}

// format not used now.
int32_t setBuffersGeometry(int32_t width, int32_t height) {
    int32_t format = WINDOW_FORMAT_RGB_565;
    if (NULL == mANativeWindow) {
        LOGV("mANativeWindow is NULL.");
        return -1;
    }
    return ANativeWindow_setBuffersGeometry(mANativeWindow, width, height,
                                            format);
}